package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"
	"unsafe"

	"github.com/gen2brain/go-mpv"
	"github.com/schollz/progressbar/v3"
	"github.com/zSnails/peruere/geometry"
	"github.com/zSnails/peruere/xlib"
)

var (
	videoFile string
	videoURL  string
	geom      string
	tempFile  *os.File
)

func init() {
	flag.StringVar(&videoFile, "file", "", "本地视频文件路径（与-url二选一）")
	flag.StringVar(&videoURL, "url", "", "网络视频URL（与-file二选一）")
	flag.StringVar(&geom, "geometry", "1920x1080+0+0", "背景窗口尺寸（格式：宽x高+X偏移+Y偏移）")
	flag.Parse()

	if (videoFile == "" && videoURL == "") || (videoFile != "" && videoURL != "") {
		log.Fatalln("错误：必须指定 -file（本地文件）或 -url（网络URL）中的一个")
	}
}

func downloadURL(url string) (string, error) {
	log.Printf("正在连接URL：%s", url)
	resp, err := http.Get(url)
	if err != nil {
		return "", fmt.Errorf("连接失败：%w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("请求失败：状态码 %d", resp.StatusCode)
	}

	log.Printf("连接成功，状态码：%d", resp.StatusCode)
	contentLength := resp.Header.Get("Content-Length")
	totalSize := int64(0)
	if contentLength != "" {
		totalSize, err = strconv.ParseInt(contentLength, 10, 64)
		if err != nil {
			log.Printf("无法解析文件大小：%v", err)
			totalSize = -1
		}
	} else {
		totalSize = -1
	}

	var bar *progressbar.ProgressBar
	if totalSize > 0 {
		log.Printf("文件总大小：%.2f MB", float64(totalSize)/1024/1024)
		bar = progressbar.NewOptions64(
			totalSize,
			progressbar.OptionSetDescription("下载中..."),
			progressbar.OptionSetWidth(50),
			progressbar.OptionShowBytes(true),
			progressbar.OptionThrottle(100*time.Millisecond),
			progressbar.OptionUseANSICodes(true),
			progressbar.OptionClearOnFinish(),
		)
	} else {
		log.Println("文件大小未知，显示动态进度")
		bar = progressbar.NewOptions(
			-1,
			progressbar.OptionSetDescription("下载中..."),
			progressbar.OptionSetWidth(50),
			progressbar.OptionShowBytes(true),
			progressbar.OptionThrottle(100*time.Millisecond),
			progressbar.OptionUseANSICodes(true),
			progressbar.OptionClearOnFinish(),
		)
	}

	tempDir := os.TempDir()
	localTempFile, err := os.CreateTemp(tempDir, "peruere-*.mp4")
	if err != nil {
		return "", fmt.Errorf("创建临时文件失败：%w", err)
	}

	tempFile = localTempFile

	writer := io.MultiWriter(localTempFile, bar)
	_, err = io.Copy(writer, resp.Body)
	if err != nil {
		return "", fmt.Errorf("下载写入失败：%w", err)
	}

	bar.Finish()
	log.Println("下载完成")

	if _, err := localTempFile.Seek(0, io.SeekStart); err != nil {
		return "", fmt.Errorf("文件指针重置失败：%w", err)
	}

	log.Printf("临时文件路径：%s", localTempFile.Name())
	return localTempFile.Name(), nil
}

func cleanupTempFile() {
	if tempFile != nil {
		tempPath := tempFile.Name()
		if err := tempFile.Close(); err != nil {
			log.Printf("关闭临时文件失败：%v", err)
		}
		if err := os.Remove(tempPath); err != nil {
			log.Printf("删除临时文件失败：%v", err)
		} else {
			log.Printf("临时文件已清理：%s", tempPath)
		}
	}
}

func main() {
	defer cleanupTempFile()

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	attrs := xlib.SetWindowAttributes{
		BackgroundPixmap: xlib.ParentRelative,
		BackingStore:     xlib.Always,
		SaveUnder:        xlib.False,
		OverrideRedirect: xlib.True,
	}

	display := xlib.XOpenDisplay(nil)
	if display == nil {
		log.Fatalln("无法打开X11显示")
	}
	defer xlib.XCloseDisplay(display)

	root := xlib.XDefaultRootWindow(display)
	width, height, xOffset, yOffset, err := geometry.ParseGeometry(geom)
	if err != nil {
		log.Fatalln("解析尺寸失败：", err)
	}

	window := xlib.XCreateWindow(display, root, xOffset, yOffset, width, height, 0, 0, xlib.InputOutput, nil, xlib.CWOverrideRedirect|xlib.CWBackingStore, &attrs)
	defer xlib.XDestroyWindow(display, window)

	xlib.XSetClassHint(
		display,
		window,
		&xlib.ClassHint{
			ResName:  "peruere",
			ResClass: "peruere",
		},
	)

	windowTypeDesktop := xlib.XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", xlib.False)
	windowType := xlib.XInternAtom(display, "_NET_WM_WINDOW_TYPE", xlib.False)
	xlib.XChangeProperty(display, window, windowType, xlib.XA_ATOM, 32, xlib.PropModeReplace, unsafe.Pointer(&windowTypeDesktop), 1)

	motifWmHints := xlib.XInternAtom(display, "_MOTIF_WM_HINTS", xlib.False)
	if motifWmHints != xlib.None {
		prop := [5]int64{2, 0, 0, 0, 0}
		xlib.XChangeProperty(display, window, motifWmHints, motifWmHints, 32, xlib.PropModeReplace, unsafe.Pointer(&prop), 5)
	}

	winLayer := xlib.XInternAtom(display, "_WIN_LAYER", xlib.False)
	if winLayer != xlib.None {
		layerZero := int64(0)
		xlib.XChangeProperty(display, window, winLayer, xlib.XA_CARDINAL, 32, xlib.PropModeAppend, unsafe.Pointer(&layerZero), 1)
	}

	wmState := xlib.XInternAtom(display, "_NET_WM_STATE", xlib.False)
	if wmState != xlib.None {
		stateBelow := xlib.XInternAtom(display, "_NET_WM_STATE_BELOW", xlib.False)
		xlib.XChangeProperty(display, window, wmState, xlib.XA_ATOM, 32, xlib.PropModeAppend, unsafe.Pointer(&stateBelow), 1)
	}

	hints := xlib.WMHints{
		Input: xlib.False,
	}
	xlib.XSetWMProperties(display, window, nil, nil, os.Args, len(os.Args), nil, &hints, nil)

	wmDesktop := xlib.XInternAtom(display, "_NET_WM_DESKTOP", xlib.False)
	stateSticky := xlib.XInternAtom(display, "_NET_WM_STATE_STICKY", xlib.False)
	xlib.XChangeProperty(display, window, wmDesktop, xlib.XA_CARDINAL, 32, xlib.PropModeAppend, unsafe.Pointer(&stateSticky), 1)

	region := xlib.XCreateRegion()
	if region != nil {
		xlib.XShapeCombineRegion(display, window, xlib.ShapeInput, 0, 0, region, xlib.ShapeSet)
		xlib.XDestroyRegion(region)
	}

	xlib.XLowerWindow(display, window)

	m := mpv.New()
	defer m.TerminateDestroy()

	if err := m.SetProperty("wid", mpv.FormatInt64, int(window)); err != nil {
		log.Fatalln("设置窗口句柄失败：", err)
	}

	if err := m.SetPropertyString("loop", "yes"); err != nil {
		log.Fatalln("设置循环播放失败：", err)
	}

	if err := m.SetPropertyString("x11-bypass-compositor", "yes"); err != nil {
		log.Fatalln("设置绕过 compositor 失败：", err)
	}

	if err := m.SetPropertyString("vo", "gpu"); err != nil {
		log.Fatalln("设置视频输出失败：", err)
	}

	if err := m.Initialize(); err != nil {
		log.Fatalln("初始化 mpv 失败：", err)
	}

	if err := m.RequestLogMessages("trace"); err != nil {
		log.Fatalln("请求日志失败：", err)
	}

	var playPath string
	if videoURL != "" {
		playPath, err = downloadURL(videoURL)
		if err != nil {
			log.Fatalln("处理URL失败：", err)
		}
	} else {
		if videoFile == "" {
			videoFile = "video.mp4"
		}
		playPath = videoFile
		if _, err := os.Stat(playPath); os.IsNotExist(err) {
			log.Fatalln("本地文件不存在：", playPath)
		}
	}

	if err := m.Command([]string{"loadfile", playPath}); err != nil {
		log.Fatalln("加载视频失败：", err)
	}

	quit := make(chan struct{})
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-quit:
				return
			default:
				event := m.WaitEvent(100)
				if event == nil {
					continue
				}
				switch event.EventID {
				case mpv.EventLogMsg:
					log.Printf("mpv日志：%v", event.LogMessage())
				case mpv.EventShutdown:
					log.Println("mpv 已关闭")
					return
				}
			}
		}
	}()

	xlib.XStoreName(display, window, "peruere")
	xlib.XMapWindow(display, window)
	xlib.XFlush(display)

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
	<-sig
	log.Println("收到退出信号，正在清理...")

	close(quit)
	wg.Wait()
}
